{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "7a0d64f6",
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "import seaborn as sns\n",
    "from sklearn.model_selection import StratifiedKFold\n",
    "from sklearn.preprocessing import StandardScaler, LabelEncoder\n",
    "from sklearn.linear_model import LogisticRegression\n",
    "from sklearn.metrics import classification_report, roc_auc_score, confusion_matrix\n",
    "import xgboost as xgb\n",
    "from colorama import init, Fore, Style\n",
    "import files  # Import the files module for dataset paths"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "0a0605ff",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "===========================================\n",
      " PPG Blood Glucose - JB Implementation\n",
      " Training Logistic Regression and XGBoost\n",
      "===========================================\n",
      "\n",
      "Step 1: Loading and merging datasets...\n",
      "Datasets loaded successfully!\n",
      "PPG_MY_OWN columns: ['subject_ID', 'num_segments', 'mean_ibi', 'sdnn', 'rmssd', 'length_to_max_ratio', 'spectral_entropy']\n",
      "METADATA_PATH columns: ['subject_ID', 'Sex', 'Age', 'BMI', 'Heart Rate', 'diabetes_label']\n",
      "Merged dataset shape: (219, 12)\n",
      "\n",
      "Step 2: Preprocessing the data...\n",
      "Encoding target labels...\n",
      "Target labels encoded: 'No' -> 0, 'Yes' -> 1\n",
      "Handling missing values...\n",
      "No missing values remain after preprocessing.\n",
      "Scaling features...\n",
      "Features scaled successfully!\n",
      "\n",
      "Step 3: Setting up 5-fold stratified cross-validation...\n",
      "\n",
      "Step 4: Training and evaluating models...\n",
      "Processing fold 1/5..."
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "d:\\trash)stuff\\PPG_BLOOD_GLUCOSE\\JB_implementation\\JB\\Lib\\site-packages\\xgboost\\training.py:183: UserWarning: [11:30:52] WARNING: C:\\actions-runner\\_work\\xgboost\\xgboost\\src\\learner.cc:738: \n",
      "Parameters: { \"use_label_encoder\" } are not used.\n",
      "\n",
      "  bst.update(dtrain, iteration=i, fobj=obj)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      " Done!\n",
      "Processing fold 2/5..."
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "d:\\trash)stuff\\PPG_BLOOD_GLUCOSE\\JB_implementation\\JB\\Lib\\site-packages\\xgboost\\training.py:183: UserWarning: [11:30:53] WARNING: C:\\actions-runner\\_work\\xgboost\\xgboost\\src\\learner.cc:738: \n",
      "Parameters: { \"use_label_encoder\" } are not used.\n",
      "\n",
      "  bst.update(dtrain, iteration=i, fobj=obj)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      " Done!\n",
      "Processing fold 3/5..."
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "d:\\trash)stuff\\PPG_BLOOD_GLUCOSE\\JB_implementation\\JB\\Lib\\site-packages\\xgboost\\training.py:183: UserWarning: [11:30:53] WARNING: C:\\actions-runner\\_work\\xgboost\\xgboost\\src\\learner.cc:738: \n",
      "Parameters: { \"use_label_encoder\" } are not used.\n",
      "\n",
      "  bst.update(dtrain, iteration=i, fobj=obj)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      " Done!\n",
      "Processing fold 4/5..."
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "d:\\trash)stuff\\PPG_BLOOD_GLUCOSE\\JB_implementation\\JB\\Lib\\site-packages\\xgboost\\training.py:183: UserWarning: [11:30:53] WARNING: C:\\actions-runner\\_work\\xgboost\\xgboost\\src\\learner.cc:738: \n",
      "Parameters: { \"use_label_encoder\" } are not used.\n",
      "\n",
      "  bst.update(dtrain, iteration=i, fobj=obj)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      " Done!\n",
      "Processing fold 5/5..."
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "d:\\trash)stuff\\PPG_BLOOD_GLUCOSE\\JB_implementation\\JB\\Lib\\site-packages\\xgboost\\training.py:183: UserWarning: [11:30:53] WARNING: C:\\actions-runner\\_work\\xgboost\\xgboost\\src\\learner.cc:738: \n",
      "Parameters: { \"use_label_encoder\" } are not used.\n",
      "\n",
      "  bst.update(dtrain, iteration=i, fobj=obj)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      " Done!\n",
      "\n",
      "Step 5: Visualizing results...\n",
      "\n",
      "Step 6: Analyzing feature importance for XGBoost...\n",
      "\n",
      "===========================================\n",
      "Training and evaluation completed successfully!\n",
      "Plots saved in the 'outputs/' directory:\n",
      "- Model Performance: model_performance_comparison.png\n",
      "- Confusion Matrix (Logistic): confusion_matrix_logistic.png\n",
      "- Confusion Matrix (XGBoost): confusion_matrix_xgboost.png\n",
      "- Feature Importance (XGBoost): feature_importance_xgboost.png\n",
      "===========================================\n",
      "\n",
      "Step 6: Classification Reports (Last Fold)\n",
      "\n",
      "Logistic Regression Classification Report (Last Fold):\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "d:\\trash)stuff\\PPG_BLOOD_GLUCOSE\\JB_implementation\\JB\\Lib\\site-packages\\sklearn\\metrics\\_classification.py:1565: UndefinedMetricWarning: Precision is ill-defined and being set to 0.0 in labels with no predicted samples. Use `zero_division` parameter to control this behavior.\n",
      "  _warn_prf(average, modifier, f\"{metric.capitalize()} is\", len(result))\n",
      "d:\\trash)stuff\\PPG_BLOOD_GLUCOSE\\JB_implementation\\JB\\Lib\\site-packages\\sklearn\\metrics\\_classification.py:1565: UndefinedMetricWarning: Precision is ill-defined and being set to 0.0 in labels with no predicted samples. Use `zero_division` parameter to control this behavior.\n",
      "  _warn_prf(average, modifier, f\"{metric.capitalize()} is\", len(result))\n",
      "d:\\trash)stuff\\PPG_BLOOD_GLUCOSE\\JB_implementation\\JB\\Lib\\site-packages\\sklearn\\metrics\\_classification.py:1565: UndefinedMetricWarning: Precision is ill-defined and being set to 0.0 in labels with no predicted samples. Use `zero_division` parameter to control this behavior.\n",
      "  _warn_prf(average, modifier, f\"{metric.capitalize()} is\", len(result))\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "              precision    recall  f1-score   support\n",
      "\n",
      "          No       0.84      1.00      0.91        36\n",
      "         Yes       0.00      0.00      0.00         7\n",
      "\n",
      "    accuracy                           0.84        43\n",
      "   macro avg       0.42      0.50      0.46        43\n",
      "weighted avg       0.70      0.84      0.76        43\n",
      "\n",
      "\n",
      "XGBoost Classification Report (Last Fold):\n",
      "              precision    recall  f1-score   support\n",
      "\n",
      "          No       0.81      0.83      0.82        36\n",
      "         Yes       0.00      0.00      0.00         7\n",
      "\n",
      "    accuracy                           0.70        43\n",
      "   macro avg       0.41      0.42      0.41        43\n",
      "weighted avg       0.68      0.70      0.69        43\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# Initialize colorama for colored outputs\n",
    "init(autoreset=True)\n",
    "\n",
    "# Set random seed for reproducibility\n",
    "np.random.seed(42)\n",
    "\n",
    "# Print a welcome message with project details\n",
    "print(Fore.CYAN + Style.BRIGHT + \"===========================================\")\n",
    "print(Fore.CYAN + Style.BRIGHT + \" PPG Blood Glucose - JB Implementation\")\n",
    "print(Fore.CYAN + Style.BRIGHT + \" Training Logistic Regression and XGBoost\")\n",
    "print(Fore.CYAN + Style.BRIGHT + \"===========================================\\n\")\n",
    "\n",
    "# Step 1: Load and merge the datasets\n",
    "print(Fore.MAGENTA + \"Step 1: Loading and merging datasets...\")\n",
    "try:\n",
    "    # Load datasets using paths from files module\n",
    "    ppg_data = pd.read_csv(files.PPG_MY_OWN)\n",
    "    metadata = pd.read_csv(files.METADATA_PATH)\n",
    "    print(Fore.GREEN + \"Datasets loaded successfully!\")\n",
    "except FileNotFoundError as e:\n",
    "    print(Fore.RED + f\"Error: {e}\")\n",
    "    print(Fore.RED + \"Please ensure the dataset files exist in the 'datasets/' directory.\")\n",
    "    raise Exception(\"File loading failed\")\n",
    "\n",
    "# Inspect column names for debugging\n",
    "print(Fore.YELLOW + \"PPG_MY_OWN columns: \" + str(ppg_data.columns.tolist()))\n",
    "print(Fore.YELLOW + \"METADATA_PATH columns: \" + str(metadata.columns.tolist()))\n",
    "\n",
    "# Define the merge column (update this based on your dataset)\n",
    "merge_column = 'subject_ID'  # Adjust this based on the actual column name\n",
    "\n",
    "# Check if the merge column exists\n",
    "if merge_column not in ppg_data.columns or merge_column not in metadata.columns:\n",
    "    print(Fore.RED + f\"Error: '{merge_column}' column not found in one or both datasets.\")\n",
    "    print(Fore.RED + \"Please check the column names above and update the 'merge_column' variable.\")\n",
    "    raise Exception(\"Merge column not found\")\n",
    "\n",
    "# Merge datasets\n",
    "try:\n",
    "    data = pd.merge(ppg_data, metadata, on=merge_column, how='inner')\n",
    "    print(Fore.GREEN + f\"Merged dataset shape: {data.shape}\")\n",
    "except KeyError as e:\n",
    "    print(Fore.RED + f\"Error: {e}\")\n",
    "    print(Fore.RED + f\"Ensure both datasets have a '{merge_column}' column for merging.\")\n",
    "    raise Exception(\"Merge failed\")\n",
    "\n",
    "# Step 2: Preprocess the data\n",
    "print(Fore.MAGENTA + \"\\nStep 2: Preprocessing the data...\")\n",
    "# Define the target column (update this based on your dataset)\n",
    "target_column = 'diabetes_label'  # Adjust this if the column name is different\n",
    "\n",
    "# Check if the target column exists\n",
    "if target_column not in data.columns:\n",
    "    print(Fore.RED + f\"Error: '{target_column}' column not found in the merged dataset.\")\n",
    "    print(Fore.RED + \"Please check the column names above and update the 'target_column' variable.\")\n",
    "    raise Exception(\"Target column not found\")\n",
    "\n",
    "# Define the features to use\n",
    "required_features = ['mean_ibi', 'sdnn', 'rmssd', 'length_to_max_ratio', 'spectral_entropy']\n",
    "\n",
    "# Check if all required features exist in the dataset\n",
    "missing_features = [feat for feat in required_features if feat not in data.columns]\n",
    "if missing_features:\n",
    "    print(Fore.RED + f\"Error: The following required features are missing: {missing_features}\")\n",
    "    print(Fore.RED + \"Please check the column names above and ensure these features are present.\")\n",
    "    raise Exception(\"Required features missing\")\n",
    "\n",
    "# Separate features and target, using only the specified features\n",
    "try:\n",
    "    X = data[required_features]  # Select only the specified features\n",
    "    y = data[target_column]\n",
    "except KeyError as e:\n",
    "    print(Fore.RED + f\"Error: {e}\")\n",
    "    print(Fore.RED + f\"Ensure the '{target_column}' column exists in the dataset.\")\n",
    "    raise Exception(\"Column selection failed\")\n",
    "\n",
    "# Encode the target labels ('Yes'/'No' to 1/0)\n",
    "print(Fore.YELLOW + \"Encoding target labels...\")\n",
    "try:\n",
    "    label_encoder = LabelEncoder()\n",
    "    y = label_encoder.fit_transform(y)\n",
    "    print(Fore.GREEN + \"Target labels encoded: 'No' -> 0, 'Yes' -> 1\")\n",
    "except ValueError as e:\n",
    "    print(Fore.RED + f\"Error during label encoding: {e}\")\n",
    "    print(Fore.RED + \"Ensure the target column contains valid categorical values (e.g., 'Yes'/'No').\")\n",
    "    raise Exception(\"Label encoding failed\")\n",
    "\n",
    "# Handle missing values by filling with the mean\n",
    "print(Fore.YELLOW + \"Handling missing values...\")\n",
    "X = X.fillna(X.mean())\n",
    "if X.isnull().sum().sum() == 0:\n",
    "    print(Fore.GREEN + \"No missing values remain after preprocessing.\")\n",
    "else:\n",
    "    print(Fore.RED + \"Warning: Some missing values still exist after preprocessing!\")\n",
    "\n",
    "# Scale the features\n",
    "print(Fore.YELLOW + \"Scaling features...\")\n",
    "scaler = StandardScaler()\n",
    "X_scaled = scaler.fit_transform(X)\n",
    "print(Fore.GREEN + \"Features scaled successfully!\")\n",
    "\n",
    "# Step 3: Set up stratified k-fold cross-validation\n",
    "n_folds = 5\n",
    "print(Fore.MAGENTA + f\"\\nStep 3: Setting up {n_folds}-fold stratified cross-validation...\")\n",
    "skf = StratifiedKFold(n_splits=n_folds, shuffle=True, random_state=42)\n",
    "\n",
    "# Initialize models\n",
    "logistic_model = LogisticRegression(random_state=42, max_iter=1000)\n",
    "xgb_model = xgb.XGBClassifier(use_label_encoder=False, eval_metric='logloss', random_state=42)\n",
    "\n",
    "# Lists to store results\n",
    "logistic_cv_scores = []\n",
    "xgb_cv_scores = []\n",
    "logistic_roc_scores = []\n",
    "xgb_roc_scores = []\n",
    "logistic_conf_matrices = []\n",
    "xgb_conf_matrices = []\n",
    "\n",
    "# Step 4: Train and evaluate models using cross-validation\n",
    "print(Fore.MAGENTA + \"\\nStep 4: Training and evaluating models...\")\n",
    "fold = 1\n",
    "for train_idx, test_idx in skf.split(X_scaled, y):\n",
    "    print(Fore.CYAN + f\"Processing fold {fold}/{n_folds}...\", end=\"\")\n",
    "    X_train, X_test = X_scaled[train_idx], X_scaled[test_idx]\n",
    "    y_train, y_test = y[train_idx], y[test_idx]\n",
    "\n",
    "    # Logistic Regression\n",
    "    logistic_model.fit(X_train, y_train)\n",
    "    logistic_pred = logistic_model.predict(X_test)\n",
    "    logistic_prob = logistic_model.predict_proba(X_test)[:, 1]\n",
    "    logistic_cv_scores.append(logistic_model.score(X_test, y_test))\n",
    "    logistic_roc_scores.append(roc_auc_score(y_test, logistic_prob))\n",
    "    logistic_conf_matrices.append(confusion_matrix(y_test, logistic_pred))\n",
    "\n",
    "    # XGBoost\n",
    "    xgb_model.fit(X_train, y_train)\n",
    "    xgb_pred = xgb_model.predict(X_test)\n",
    "    xgb_prob = xgb_model.predict_proba(X_test)[:, 1]\n",
    "    xgb_cv_scores.append(xgb_model.score(X_test, y_test))\n",
    "    xgb_roc_scores.append(roc_auc_score(y_test, xgb_prob))\n",
    "    xgb_conf_matrices.append(confusion_matrix(y_test, xgb_pred))\n",
    "\n",
    "    print(Fore.GREEN + \" Done!\")\n",
    "    fold += 1\n",
    "\n",
    "# Step 5: Visualize results using matplotlib\n",
    "print(Fore.MAGENTA + \"\\nStep 5: Visualizing results...\")\n",
    "\n",
    "# Plot 1: Bar chart for CV Accuracy and ROC AUC\n",
    "plt.figure(figsize=(8, 6))\n",
    "models = ['Logistic Regression', 'XGBoost']\n",
    "cv_accuracies = [np.mean(logistic_cv_scores), np.mean(xgb_cv_scores)]\n",
    "roc_aucs = [np.mean(logistic_roc_scores), np.mean(xgb_roc_scores)]\n",
    "\n",
    "x = np.arange(len(models))\n",
    "width = 0.35\n",
    "\n",
    "plt.bar(x - width/2, cv_accuracies, width, label='CV Accuracy', color='skyblue')\n",
    "plt.bar(x + width/2, roc_aucs, width, label='ROC AUC', color='lightcoral')\n",
    "plt.xlabel('Models')\n",
    "plt.ylabel('Scores')\n",
    "plt.title('Model Performance Comparison')\n",
    "plt.xticks(x, models)\n",
    "plt.legend()\n",
    "plt.tight_layout()\n",
    "plt.savefig('model_performance_comparison.png')\n",
    "plt.close()\n",
    "\n",
    "# Plot 2: Confusion Matrix Heatmap for Logistic Regression\n",
    "plt.figure(figsize=(6, 5))\n",
    "sns.heatmap(logistic_conf_matrices[-1], annot=True, fmt='d', cmap='Blues', \n",
    "            xticklabels=['No', 'Yes'], yticklabels=['No', 'Yes'])\n",
    "plt.title('Confusion Matrix - Logistic Regression (Last Fold)')\n",
    "plt.xlabel('Predicted')\n",
    "plt.ylabel('True')\n",
    "plt.tight_layout()\n",
    "plt.savefig('confusion_matrix_logistic.png')\n",
    "plt.close()\n",
    "\n",
    "# Plot 3: Confusion Matrix Heatmap for XGBoost\n",
    "plt.figure(figsize=(6, 5))\n",
    "sns.heatmap(xgb_conf_matrices[-1], annot=True, fmt='d', cmap='Greens', \n",
    "            xticklabels=['No', 'Yes'], yticklabels=['No', 'Yes'])\n",
    "plt.title('Confusion Matrix - XGBoost (Last Fold)')\n",
    "plt.xlabel('Predicted')\n",
    "plt.ylabel('True')\n",
    "plt.tight_layout()\n",
    "plt.savefig('confusion_matrix_xgboost.png')\n",
    "plt.close()\n",
    "\n",
    "# Step 6: Feature Importance for XGBoost\n",
    "print(Fore.MAGENTA + \"\\nStep 6: Analyzing feature importance for XGBoost...\")\n",
    "feature_names = X.columns\n",
    "feature_importance = xgb_model.feature_importances_\n",
    "sorted_idx = np.argsort(feature_importance)[::-1]\n",
    "top_n = min(5, len(feature_names))  # Already limited to 5 features\n",
    "\n",
    "# Plot feature importance\n",
    "plt.figure(figsize=(8, 6))\n",
    "plt.barh(range(top_n), feature_importance[sorted_idx[:top_n]], align='center', color='lightgreen')\n",
    "plt.yticks(range(top_n), [feature_names[i] for i in sorted_idx[:top_n]])\n",
    "plt.xlabel('Feature Importance')\n",
    "plt.title('Feature Importance - XGBoost')\n",
    "plt.tight_layout()\n",
    "plt.savefig('feature_importance_xgboost.png')\n",
    "plt.close()\n",
    "\n",
    "# Print a message about where to find the plots\n",
    "print(Fore.CYAN + Style.BRIGHT + \"\\n===========================================\")\n",
    "print(Fore.GREEN + Style.BRIGHT + \"Training and evaluation completed successfully!\")\n",
    "print(Fore.GREEN + \"Plots saved in the 'outputs/' directory:\")\n",
    "print(Fore.GREEN + \"- Model Performance: model_performance_comparison.png\")\n",
    "print(Fore.GREEN + \"- Confusion Matrix (Logistic): confusion_matrix_logistic.png\")\n",
    "print(Fore.GREEN + \"- Confusion Matrix (XGBoost): confusion_matrix_xgboost.png\")\n",
    "print(Fore.GREEN + \"- Feature Importance (XGBoost): feature_importance_xgboost.png\")\n",
    "print(Fore.CYAN + Style.BRIGHT + \"===========================================\")\n",
    "# After cross-validation, print classification reports for the last fold\n",
    "\n",
    "print(Fore.MAGENTA + \"\\nStep 6: Classification Reports (Last Fold)\")\n",
    "\n",
    "# Logistic Regression classification report\n",
    "print(Fore.CYAN + \"\\nLogistic Regression Classification Report (Last Fold):\")\n",
    "print(classification_report(y_test, logistic_pred, target_names=['No', 'Yes']))\n",
    "\n",
    "# XGBoost classification report\n",
    "print(Fore.CYAN + \"\\nXGBoost Classification Report (Last Fold):\")\n",
    "print(classification_report(y_test, xgb_pred, target_names=['No', 'Yes']))\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "JB",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.13.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
